Bug 2429 allow selection of associated namespace in recent changes
authorAntoine Musso <hashar@users.mediawiki.org>
Wed, 2 Mar 2011 20:40:40 +0000 (20:40 +0000)
committerAntoine Musso <hashar@users.mediawiki.org>
Wed, 2 Mar 2011 20:40:40 +0000 (20:40 +0000)
Done by adding yet another checkbox in Special:RecentChanges. The feature
also support namespace inversion.  For example, if you have selected
the TALK namespace with inversion and associated namespace, you will
be shown any changes which is not NS_MAIN or NS_TALK.

Tests:

SpecialRecentchanges tests only this feature. I had to filter out
the rc_timestamp condition which might cause trouble if the test
suite is run on another day. A better solution remains to be implemented.

RELEASE-NOTES
includes/specials/SpecialRecentchanges.php
languages/messages/MessagesEn.php
maintenance/language/messages.inc
tests/phpunit/includes/specials/SpecialRecentchanges.php [new file with mode: 0644]

index ea75f8d..1dcb80c 100644 (file)
@@ -81,6 +81,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
   safely enabled. A ZIP file reader was added which can scan a ZIP file for
   potentially dangerous Java applets. This allows applets to be blocked
   specifically, rather than all ZIP files being blocked.
+* (bug 2429) Allow selection of associated namespace in recent changes
 
 === Bug fixes in 1.18 ===
 * (bug 23119) WikiError class and subclasses are now marked as deprecated
index dea902b..3277902 100644 (file)
@@ -55,6 +55,7 @@ class SpecialRecentChanges extends IncludableSpecialPage {
 
                $opts->add( 'namespace', '', FormOptions::INTNULL );
                $opts->add( 'invert', false );
+               $opts->add( 'associated', false );
 
                $opts->add( 'categories', '' );
                $opts->add( 'categories_any', false );
@@ -284,13 +285,26 @@ class SpecialRecentChanges extends IncludableSpecialPage {
 
                # Namespace filtering
                if( $opts['namespace'] !== '' ) {
-                       if( !$opts['invert'] ) {
-                               $conds[] = 'rc_namespace = ' . $dbr->addQuotes( $opts['namespace'] );
+                       $selectedNS = $dbr->addQuotes( $opts['namespace'] );
+                       $operator = $opts['invert'] ? '!='  : '=';
+                       $boolean  = $opts['invert'] ? 'AND' : 'OR';
+
+                       # namespace association (bug 2429)
+                       if( !$opts['associated'] ) {
+                               $condition = "rc_namespace $operator $selectedNS";
                        } else {
-                               $conds[] = 'rc_namespace != ' . $dbr->addQuotes( $opts['namespace'] );
+                               # Also add the associated namespace
+                               $associatedNS =  $dbr->addQuotes(
+                                       MWNamespace::getAssociated( $opts['namespace'] )
+                               );
+                               $condition = "(rc_namespace $operator $selectedNS "
+                                          . $boolean
+                                          . " rc_namespace $operator $associatedNS)";
                        }
-               }
 
+                       $conds[] = $condition;
+               }
+wfVarDump( $conds );
                return $conds;
        }
 
@@ -463,7 +477,7 @@ class SpecialRecentChanges extends IncludableSpecialPage {
 
                $defaults = $opts->getAllValues();
                $nondefaults = $opts->getChangedValues();
-               $opts->consumeValues( array( 'namespace', 'invert', 'tagfilter',
+               $opts->consumeValues( array( 'namespace', 'invert', 'associated', 'tagfilter',
                        'categories', 'categories_any' ) );
 
                $panel = array();
@@ -555,6 +569,7 @@ class SpecialRecentChanges extends IncludableSpecialPage {
        /**
         * Creates the choose namespace selection
         *
+        * @todo Uses radio buttons (HASHAR)
         * @param $opts FormOptions
         * @return String
         */
@@ -562,7 +577,8 @@ class SpecialRecentChanges extends IncludableSpecialPage {
                $nsSelect = Xml::namespaceSelector( $opts['namespace'], '' );
                $nsLabel = Xml::label( wfMsg('namespace'), 'namespace' );
                $invert = Xml::checkLabel( wfMsg('invert'), 'invert', 'nsinvert', $opts['invert'] );
-               return array( $nsLabel, "$nsSelect $invert" );
+               $associated = Xml::checkLabel( wfMsg('namespace_association'), 'associated', 'nsassociated', $opts['associated'] );
+               return array( $nsLabel, "$nsSelect $invert $associated" );
        }
 
        /**
index 68650e6..2465c21 100644 (file)
@@ -2964,6 +2964,7 @@ $1',
 # Namespace form on various pages
 'namespace'      => 'Namespace:',
 'invert'         => 'Invert selection',
+'namespace_association' => 'Associated namespace',
 'blanknamespace' => '(Main)',
 
 # Contributions
index 2bf4ad5..6247843 100644 (file)
@@ -1989,6 +1989,7 @@ $wgMessageStructure = array(
        'nsform' => array(
                'namespace',
                'invert',
+               'namespace_association',
                'blanknamespace',
        ),
        'contributions' => array(
diff --git a/tests/phpunit/includes/specials/SpecialRecentchanges.php b/tests/phpunit/includes/specials/SpecialRecentchanges.php
new file mode 100644 (file)
index 0000000..48e31f3
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Test class for SpecialRecentchanges class
+ *
+ * Copyright © 2011, Ashar Voultoiz
+ *
+ * @author Ashar Voultoiz
+ */
+class SpecialRecentchangesTest extends MediaWikiTestCase {
+
+       /**
+        * @var SpecialRecentChanges
+        */
+       protected $rc;
+
+       function setUp() {
+       }
+
+       /** helper to test SpecialRecentchanges::buildMainQueryConds() */
+       private function assertConditions( $expected, $requestOptions = null, $message = '' ) {
+               global $wgRequest;
+               $savedGlobal = $wgRequest;
+
+               # Initialize a WebRequest object ...
+               $wgRequest = new FauxRequest( $requestOptions );
+               # ... then setup the rc object (which use wgRequest internally)
+               $this->rc = new SpecialRecentChanges();
+               $formOptions = $this->rc->setup( null );
+
+               # Filter out rc_timestamp conditions which depends on the test runtime
+               # This condition is not needed as of march 2, 2011 -- hashar
+               # FIXME: find a way to generate the correct rc_timestamp
+               $queryConditions = array_filter(
+                       $this->rc->buildMainQueryConds( $formOptions ),
+                       'SpecialRecentchangesTest::filterOutRcTimestampCondition'
+               );
+
+               $this->assertEquals(
+                       $expected,
+                       $queryConditions,
+                       $message
+               );
+
+               $wgRequest = $savedGlobal;
+       }
+
+       /** return false if condition begin with 'rc_timestamp ' */
+       private static function filterOutRcTimestampCondition( $var ) {
+               return (false === strpos( $var, 'rc_timestamp ' ));
+
+       }
+
+       public function testRcNsFilter() {
+               $this->assertConditions(
+                       array( # expected
+                               'rc_bot' => 0,
+                               #0 => "rc_timestamp >= '20110223000000'",
+                               1 => "rc_namespace = '0'",
+                       ),
+                       array(
+                               'namespace' => NS_MAIN,
+                       ),
+                       "rc conditions with no options (aka default setting)"
+               );
+       }
+
+       public function testRcNsFilterInversion() {
+               $this->assertConditions(
+                       array( # expected
+                               #0 => "rc_timestamp >= '20110223000000'",
+                               'rc_bot' => 0,
+                               1 => sprintf( "rc_namespace != '%s'", NS_MAIN ),
+                       ),
+                       array(
+                               'namespace' => NS_MAIN,
+                               'invert' => 1,
+                       ),
+                 "rc conditions with namespace inverted"
+               );
+       }
+
+       /**
+        * @bug 2429
+        * @dataProvider provideNamespacesAssociations
+        */
+       public function testRcNsFilterAssociation( $ns1, $ns2 ) {
+               $this->assertConditions(
+                       array( # expected
+                               #0 => "rc_timestamp >= '20110223000000'",
+                               'rc_bot' => 0,
+                               1 => sprintf( "(rc_namespace = '%s' OR rc_namespace = '%s')", $ns1, $ns2 ),
+                       ),
+                       array(
+                               'namespace' => $ns1,
+                               'associated' => 1,
+                       ),
+                 "rc conditions with namespace inverted"
+               );
+       }
+
+       /**
+        * @bug 2429
+        * @dataProvider provideNamespacesAssociations
+        */
+       public function testRcNsFilterAssociationWithInversion( $ns1, $ns2 ) {
+               $this->assertConditions(
+                       array( # expected
+                               #0 => "rc_timestamp >= '20110223000000'",
+                               'rc_bot' => 0,
+                               1 => sprintf( "(rc_namespace != '%s' AND rc_namespace != '%s')", $ns1, $ns2 ),
+                       ),
+                       array(
+                               'namespace'  => $ns1,
+                               'associated' => 1,
+                               'invert'     => 1,
+                       ),
+                 "rc conditions with namespace inverted"
+               );
+       }
+
+       /**
+        * Provides associated namespaces to test recent changes
+        * namespaces association filtering.
+        */
+       public function provideNamespacesAssociations() {
+               return array( # (NS => Associated_NS)
+                       array( NS_MAIN, NS_TALK),
+                       array( NS_TALK, NS_MAIN),
+               );
+       }
+
+}
+
+